#!/usr/bin/expect --
#
# Description:
#   Condition a Cluster node, i.e. 
#   - list the network adapter information such as hardware address, physical 
#     location code;
#   - network boot.
#   This version is for HMC attached systems
#
# Outputs:
#   Network adapter information writtent to stdout
#   Progress and error information written to stderr.
#
# Syntax:
#    Usage: Install partition
#           lpar_netboot [-n] [-v] [-x] [-f] [-A -D | [-D] -l phys_loc | [-D] -m macaddress] [-g args] -t ent -s speed -d duplex
#                        -S server -G gateway -C client hostname profile managed_system
#
#    Usage: Install for system supporting full system partition
#           lpar_netboot [-n] [-v] [-x] [-f] [-A -D | [-D] -l phys_loc | [-D] -m macaddress] [-g args] -t ent -s speed -d duplex
#                        -S server -G gateway -C client managed_system managed_system
#
#    Usage: Return macaddress
#           lpar_netboot -M -n [-v] [-x] [-f] [-A] -t ent [-D -s speed -d duplex -S server -G gateway -C client] hostname profile managed_system 
#
#    Usage: Return macaddress for system supporting full system partition
#           lpar_netboot -M -n [-v] [-x] [-f] [-A] -t ent [-D -s speed -d duplex -S server -G gateway -C client] managed_system managed_system
#
#     -n  Do not do network boot. Instead, power off the node after writing
#         the adapter information to stdout
#
#     -t  Specifies that only network adapters of type <type> will be returned.
#         <type> may be "ent", "tok", or "fddi".
#
#     -D  Perform ping test.  Use adapter upon a successful ping test.
#
#     -s  network adapter speed
#
#     -d  network adapter duplex
#
#     -A  for discovery, return all adapters of the given type that successfully
#         ping the given server
#
#     -S  server ip address to ping to
#
#     -G  gateway ip address to ping through
#
#     -C  client ip address
#
#     -v  verbose
#
#     -M  discover et hernet adapter mac address and location code
#    
#     -m  use the following macaddress to perform the install
#
#     -l  use the following physical location to perform the install
#
#     -g  generic arguments for booting
# 
#     -x  enable debug output
#
#     -f  force close virtual terminal session
#
#    
# The chrp interface:
#   CHRP is an industry standard that defines interfaces for boot firmware. It
#   is defined in IEEE 1275-1994 Standard for Boot (Initialization 
#   Configuration) Firmware, with extensions for the RS/6000. At a very high
#   level, it defines a device tree, where the properties of all IO devices
#   are stored, and a command line interface for displaying and changing these 
#   properties. 
#
#   This version of nodecond uses the Open Firmware commands at the command
#   line. The commands themselves are not very intuitive. Most of
#   the commands use a stack, which means that the command verb uses arguments
#   that were previously put on the stack and leaves results on the stack. 
#   From a practical point of view this means that the command verb is the   
#   last word on a command and that additional commands are needed to dump the
#   results of the command  off the stack. However, not all commands are this 
#   way. Some commands have the command verb first with arguements following.
#   More comments on how this works are in the get_mac_addr procedure.
#   
# Debug notes: 
#   The fastest way to debug the patterns in this script is by using the -d
#   flag. This displays the buffer, patterns, and whether a pattern matched
#   or not everytime additional data comes into expect. 
#
#   The main line code follows all procedures. 

log_user 0
set stm 1

#
# PROCEDURE
#
# Declare procedure to write status/error messages
# We do it this way so that if /var is full (or some other error occurs)
# we can trap it in a common section of code.
#
proc nc_msg { msg } {
    global verbose

    if { $verbose == 1 } {
        send_user $msg
    }
}

#
# PROCEDURE
#
# Declare procedure to write usage message and exit
#
proc usage {} {
    global PROGRAM
    global DSPMSG
    global CATALOG

    system "$DSPMSG -s 1 $CATALOG 63 \
           'Usage: Install partition \
            \n\t%s \[-v\] \[-x\] \[-f\] \[-g args\] \[-A -D | \[-D\] -l phys_loc | \[-D\] -m macaddress\] -t ent -s speed -d duplex \
            \n\t\t-S server -G gateway -C client hostname profile managed_system \
            \n \
            \nUsage: Install for system supporting full system partition \
            \n\t%s \[-v\] \[-x\] \[-f\] \[-g args\] \[-A -D | \[-D\] -l phys_loc | \[-D\] -m macaddress\] -t ent -s speed -d duplex \
            \n\t\t-S server -G gateway -C client managed_system managed_system \
            \n \
            \nUsage: Return macaddress \
            \n\t%s -M -n \[-v\] \[-x\] \[-f\] \[-A\] -t ent \[-D -s speed -d duplex -S server -G gateway -C client\] hostname profile managed_system \
            \n \
            \nUsage: Return macaddress for system supporting full system partition \
            \n\t%s -M -n \[-v\] \[-x\] \[-f\] -t ent \[-D -s speed -d duplex -S server -G gateway -C client\] managed_system managed_system \
            \n \
            \n\t-n\tDo not boot partition \
            \n\t-t\tSpecifies network type ent \
            \n\t-D\tPerform ping test, use adapter that successfully ping the server \
            \n\t-A\tReturn all adapters of the given type \
            \n\t-s\tNetwork adapter speed \
            \n\t-d\tNetwork adapter duplex \
            \n\t-S\tServer IP address \
            \n\t-G\tGateway IP address \
            \n\t-C\tClient IP address \
            \n\t-l\tPhysical location code \
            \n\t-m\tMAC Address \
            \n\t-v\tVerbose output \
            \n\t-x\tDebug output \
            \n\t-f\tForce close virtual terminal session \
            \n\t-M\tDiscovery ethernet adapter mac address and location code \
            \n\t-g\tGeneric arguments for booting\n' \
            \"$PROGRAM\" \"$PROGRAM\" \"$PROGRAM\" \"$PROGRAM\" 2>/dev/null"

    exit 1
}


#
# PROCEDURE
#
# Determine if the platform is a regatta or a squadron since the command 
# usage are different.
#
proc ck_plat {} {
    global CATALOG
    global PROGRAM
    global SQUADRON 
    global BINPATH

    set rc [catch {eval exec $BINPATH/lshmc -V | grep "Version" | cut -f2 -d: } msg]

    if { $rc } {
        nc_msg "$PROGRAM Status: error from lshmc command\n"
        system "$DSPMSG -s 1 $CATALOG 39 '%s: Error : %s\n' \"$PROGRAM\" \"$msg\" 2>/dev/null"
        exit 1
    } else {
        if { [expr ($msg >= 4) ] } {
		set SQUADRON 1
		nc_msg "$PROGRAM Status: Squadron set to true.\n"
	}
    }
}


#
# PROCEDURE
#
#
proc ck_args {} {
    global adap_speed
    global adap_duplex
    global client_ip
    global server_ip
    global gateway_ip
    global node
    global manage
    global profile
    global DSPMSG
    global CATALOG
    global PROGRAM
    global discover_macaddr
    global TYPE_FULL
    global extra_args
    global macaddress
    global phys_loc
    global discover_all
    global discovery
    global noboot

    if { !$discover_macaddr && ( $adap_speed == "" || $adap_duplex == "" ) } {
        system "$DSPMSG -s 1 $CATALOG 02 '%s: speed and duplex required\n' \"$PROGRAM\" 2>/dev/null"
        usage 
    }

    if { !$discover_macaddr && $client_ip == "" } {
        system "$DSPMSG -s 1 $CATALOG 03 '%s: client IP is required\n' \"$PROGRAM\" 2>/dev/null"
        usage
    }

    if { !$discover_macaddr && $server_ip == "" } { 
        system "$DSPMSG -s 1 $CATALOG 04 '%s: server IP is required\n' \"$PROGRAM\" 2>/dev/null"
        usage
    }

    if { !$discover_macaddr && $gateway_ip == "" } {
        system "$DSPMSG -s 1 $CATALOG 05 '%s: gateway IP is required\n' \"$PROGRAM\" 2>/dev/null"
        usage
    }

    if { $node == "" } {
        system "$DSPMSG -s 1 $CATALOG 06 '%s: node is required\n' \"$PROGRAM\" 2>/dev/null"
        usage
    } else {
        nc_msg "$PROGRAM Status: node $node\n"
    }

    if { $manage == "" } {
        system "$DSPMSG -s 1 $CATALOG 07 '%s: managed system is required\n' \"$PROGRAM\" 2>/dev/null"
        usage
    } else {
        nc_msg "$PROGRAM Status: managed system $manage\n"
    }

    if { !$discover_macaddr && $profile == "" && !$TYPE_FULL } {
        system "$DSPMSG -s 1 $CATALOG 08 '%s: profile is required\n' \"$PROGRAM\" 2>/dev/null"
        usage
    } else {
        nc_msg "$PROGRAM Status: profile $profile\n"
    }

    if { $discover_macaddr && $extra_args != "" } {
        system "$DSPMSG -s 1 $CATALOG 62 '%s: can not specify -M and -g flags together.\n' \"$PROGRAM\" 2>/dev/null"
	usage
    }

    if { $discover_macaddr && ( $macaddress != "" || $phys_loc != "" ) } {
	system "$DSPMSG -s 1 $CATALOG 64 '%s: can not specify -M and -l or -m flags together.\n' \"$PROGRAM\" 2>/dev/null"
        usage
    }

    if { $macaddress != "" && $phys_loc != "" } {
        system "$DSPMSG -s 1 $CATALOG 65 '%s: can not specify -l and -m flags together.\n' \"$PROGRAM\" 2>/dev/null"
        usage
    }

    if { $discover_all && ( $macaddress != "" || $phys_loc != "" ) } {
        system "$DSPMSG -s 1 $CATALOG 66 '%s: can not specify -A and -m or -l flags together.\n' \"$PROGRAM\" 2>/dev/null"
        usage
    }
   
    if { $discover_all && !$discovery && !$noboot } {
        system "$DSPMSG -s 1 $CATALOG 67 '%s: flag -A must be specify with flag -D for booting.\n' \"$PROGRAM\" 2>/dev/null"
        usage
    }

    if { $discover_macaddr && $discovery && 
         ( $server_ip == "" || $gateway_ip == "" || $client_ip == "" || $adap_speed == "" || $adap_duplex == "" ) } {
        system "$DSPMSG -s 1 $CATALOG 68 '%s: flag -M with -D require arguments for -C, -S, -G, -s and -d.\n' \"$PROGRAM\" 2>/dev/null"
        usage
    }
   
    if { $discover_macaddr && !$discovery && 
         ( $server_ip != "" || $gateway_ip != "" || $client_ip != "" || $adap_speed != "" || $adap_duplex != "" ) } {
        system "$DSPMSG -s 1 $CATALOG 69 '%s: flag -M with arguments for -C, -S, -G, -s and -d require -D flag.\n' \"$PROGRAM\" 2>/dev/null"
        usage
    }

    if { $discover_macaddr && !$noboot } {
        system "$DSPMSG -s 1 $CATALOG 70 '%s: -M flag requires -n.\n' \"$PROGRAM\" 2>/dev/null"
        usage
    }
}

#
# PROCEDURE
# 
# Declare procedure to send commands to rconsole slowly. This is needed because
# some bytes are missed by the service processor when sent at top speed. 
# The sleep was needed because a command was sent sometimes before the 
# results of the previous command had been received.
#
# The Open Firmware is constrained on how quickly it can process input streams.
# The following code causes expect to send 10 characters and then wait 1 second
# before sending another 10 bytes.
#
proc send_command {} {

    global rc
    global command 
    global spawn_id_rconsole

    # sleep so that any error messages from previous command are cleared up
    exec sleep 1
    send -i $spawn_id_rconsole -- $command
}

#
# PROCEDURE
#
# Declare procedure to parse the full device tree that is displayed as a result
# of an ls command. The information needed is the phandle and full device name
# of a supported network card found in the device tree. The phandle is used in
# other procedures to get and change properties of the network card. The full
# device name is used to network boot from the network adapter.
# 
proc get_phandle {} {

    global spawn_id_rconsole
    global expect_out
    global phandle_array
    global full_path_name_array
    global rc
    global command
    global adapter_found
    global adap_type
    global dev_count
    global dev_pat
    global dev_type
    global DSPMSG
    global CATALOG
    global PROGRAM
    global HMCNAME
    global TYPE_FULL

    nc_msg "$PROGRAM Status: Starting to get full device name and phandle\n"

    # This is the first procedure entered after getting to the ok prompt. On entry
    # the current device is not root. The command 'dev /' is sent to get to the
    # root of the device tree. There is no output from the dev command. The expected
    # output is the ok prompt ('>').
    #
    # The pwd command can be used to determine what the current device is. 
    #
    set timeout 30     ;# shouldn't take long
    if { $TYPE_FULL } {
        sleep 30
    }
 
    nc_msg "$PROGRAM Status: sending dev / command\n"
    set command  "dev /\r"
    send_command
    expect {
        -i $spawn_id_rconsole
        -re ">" {
            nc_msg "$PROGRAM Status: at root\n"
        }
        -re "]" {
            system "$DSPMSG -s 1 $CATALOG 09 '%s: unexpected prompt\n' \"$PROGRAM\" 2>/dev/null"
            set rc 1
            return
        }
        timeout {
            system "$DSPMSG -s 1 $CATALOG 10 '%s: timeout\n' \"$PROGRAM\" 2>/dev/null"
            set rc 1
            return
        }
        eof {
            system "$DSPMSG -s 1 $CATALOG 11 '%s : cannot connect to %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
            set rc 1
            return
        }
    }

    # Next, the 'ls' command is sent. The result is a display of the entire 
    # device tree. The code then looks at the 
    # output from the ls command one line at a time, trying to match it with the
    # regexp pattern in dev_pat, an array that contains all the supported network
    # adapters. When found, the adapter type, the phandle and path name are saved 
    # in array variables. 
    #
    # The complicated part is that the full path name may be spread over more than
    # one line. Each line contains information about a node. If the supported 
    # network adapter is found on an nth level node, the full path name is the
    # concatenation of the node information from the 0th level to the nth level.
    # Hence, the path name from each level of the device tree needs to be saved.
    #
    # The pattern "\n(\[^\r]*)\r" is worth a second look. It took
    # many hours of debug and reading the expect book to get it right. When more
    # than one line of data is returned to expect at once, it is tricky getting
    # exactly one line of data to look at. This pattern works because it looks
    # for a newline(\n), any character other than a carriage return(\[^\r]*), and
    # then for a carriage return. This causes expect to match a single line.
    # If (.*) is used instead of (\[^\r]*), multiple lines are matched. (that was
    # attempt number 1)         
    #
    # Once a single line is found, it tries to determine what level in the device
    # tree this line is.  
    # searching through subsequent lines and subsequent levels until an 
    # adapter is found.  
    # The level of the current line, which
    # is calculated based on the assumption of "Level = (Leading Spaces - 1)/2".
    # Leading Spaces is the number of spaces between the first colon ':' and the
    # first non-space character of each line.
    #
    # Using the -d flag helped a lot in finding the correct pattern.  
    #
    nc_msg "$PROGRAM Status: sending ls command\n"
    set command  "ls\r"   
    send_command

    set adapter_found 0
    set done 0
    if { $TYPE_FULL } {
        sleep 30
    } 

    set timeout 60     ;# shouldn't take more than a few minutes
    while { ! $done } {
        # this expect call isolates single lines
        # This code uses the tcl regexp to parse the single line 
        # isolated by expect. 
        #
        # When the ok prompt ('>') is matched, this indicates the end of
        # the ls output, at which point the done variable is set, to break
        # out of the loop.
        #
        # All other lines are ignored.
        #  
        expect {
            -re "(\n)(\[^\r]*)(\r)" {
                if { [regexp "\n(\[^\r]*):(\[ ]+)/(\[^\r]*)\r" $expect_out(0,string) x1 x2 x3 x4] } {
                    # Each level is inspected for a match
                    set level [expr ([string length $x3]-1)/2]
                    set path($level) $x4
                    for {set j 0} {$j < $dev_count} {incr j 1} {
                        if {[regexp $dev_pat($j) $x4]} {
                            incr adapter_found 1
                            for {set i 0} {$i <= $level} {incr i 1} {
                                append full_path_name_array($adapter_found) "/$path($i)"
                            }
                            set phandle_array($adapter_found) $x2
                            set adap_type($adapter_found) $dev_type($j) 
                            break
                        }
                    }
                }
            } 
            -re ">" {
                set done 1
            }
            timeout {
                system "$DSPMSG -s 1 $CATALOG 12 '%s : timeout isolating single line of ls output\n' \"$PROGRAM\" 2>/dev/null"
                set rc 1
                return
            }
            eof {
                system "$DSPMSG -s 1 $CATALOG 11 '%s : cannot connect to %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
                set rc 1
                return
            }
        }
    }
    # Did we find one or more adapters?
    if { $adapter_found > 0 } {
        set rc 0
    } else {
        system "$DSPMSG -s 1 $CATALOG 13 '%s : No network adapters found\n' \"$PROGRAM\" 2>/dev/null"
        set rc -1
    }
}


#
# PROCEDURE
#
# Declare procedure to obtain the list of valid adapter connector properties
# from the adapter card.  Connector types can be rj45, sc, 9pin, aui,
# bnc, or mic.  Speeds can be 10, 100, or 1000.  Duplex can be half or
# full.  This procedure will use the "supported-network-types"
# argument to the get-package-property command to get the list of
# properties for the given adapter.
#

proc get_adap_prop { phandle } {

    global rc
    global spawn_id_rconsole
    global expect_out
    global command
    global adap_prop_list
    global DSPMSG
    global CATALOG
    global PROGRAM
    global HMCNAME

    set adap_prop_list {}
    set rc 0
    set timeout 60
    set state 0
    nc_msg "$PROGRAM Status: get_adap_prop start\n"

    # state 0, stack count 0
    set done(0) 0
    set cmd(0) "\" supported-network-types\" $phandle get-package-property\r"
    set msg(0) "$PROGRAM Status: rc and all supported network types now on stack\n"
    set pattern(0) "(.*)3 >(.*)"
    set newstate(0) 1

    # state 1, return code and string on stack
    set done(1) 0
    set cmd(1) ".\r"
    set msg(1) "$PROGRAM Status: All supported network types now on stack\n"
    set pattern(1) "(.*)2 >(.*)"
    set newstate(1) 2

    # state 2, data ready to decode
    set done(2) 0
    set cmd(2) "decode-string\r"
    set msg(2) "$PROGRAM Status: supported network type isolated on stack\n"
    set pattern(2) "(.*)ok(.*)4 >(.*)"
    set newstate(2) 3

    # state 3, decoded string on stack
    set done(3) 0
    set cmd(3) "dump\r"
    set msg(3) "$PROGRAM Status: supported network type off stack\n"
    set pattern(3) ".*:.*:(.*):.*:.*:(.*):.*(2 >)(.*)"
    set newstate(3) 4

    # state 4, need to check for more data to decode
    set done(4) 0
    set cmd(4) ".s\r"
    set msg(4) "$PROGRAM Status: checking for more supported network types\n"
    set pattern(4) ".s (\[0-9a-f]* )(.*)>"
    set newstate(4) 5

    # state 5, done decoding string, clear stack
    set done(5) 0
    set cmd(5) ".\r"
    set msg(5) "$PROGRAM Status: one entry on stack cleared\n"
    set pattern(5) "(.*)ok(.*)1 >(.*)"
    set newstate(5) 6

    # state 6, finish clearing stack, choose correct adapter type
    set done(6) 0
    set cmd(6) ".\r"
    set msg(6) "$PROGRAM Status: finished clearing stack\n"
    set pattern(6) "(.*)ok(.*)0 >(.*)"
    set newstate(6) 7

    # state 7, done
    set done(7) 1

    while { $done($state) == 0 } {
        nc_msg "$PROGRAM Status: command is $cmd($state)\n"
        set command $cmd($state)
        send_command
        expect {
            -i $spawn_id_rconsole
            -re $pattern($state) {
                nc_msg $msg($state)
                set state $newstate($state)

                # After state 3, the network type is parsed and the connector
                # type extracted.  If the type hasn't been found, add it to
                # the list of supported connector types.
                if { $state == 4 } {
                    set nw_type $expect_out(1,string)$expect_out(2,string)

                    #  Build the adapter properties from the string
                    regexp .*,(.*),(.*),(.*) $nw_type dummy nw_speed nw_conn nw_duplex
                    set adap_prop "$nw_speed,$nw_conn,$nw_duplex"
                    nc_msg "$PROGRAM Status: Adapter properties are $adap_prop\n"

                    # if it's not in the list, add it, otherwise continue
                    if { [ lsearch adap_prop_list $adap_prop ] == -1 } {
                        lappend adap_prop_list $adap_prop
                        nc_msg "$PROGRAM Status: Adding adapter properties to list\n"
                    }
                }

                # After state 4, a test is done to see if all of the supported
                # network types have been decoded. If they have been, the
                # state variable is left alone. if not, the state variable is
                # set to 2, causing a loop back to the step where the
                # decode-string command is sent.
                if { $state == 5 } {
                    if { [ string index $expect_out(2,string) 0] != 0 } {
                        set state 2
                   }
                }

            }
            -re "]" {
                system "$DSPMSG -s 1 $CATALOG 09 '%s: unexpected prompt\n' \"$PROGRAM\" 2>/dev/null"
                set rc 1
                return
            }
            -re "(.*)DEFAULT(.*)" {
                system "$DSPMSG -s 1 $CATALOG 14 '%s: default catch error\n' \"$PROGRAM\" 2>/dev/null"
                set rc 1
                return
            }
            timeout {
                system "$DSPMSG -s 1 $CATALOG 10 '%s: timeout\n' \"$PROGRAM\" 2>/dev/null"
                nc_msg "timeout state is $state\n"
                set rc 1
                return
            }
            eof {
                system "$DSPMSG -s 1 $CATALOG 11 '%s: cannot connect to %s' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
                set rc 1
                return
            }
        }
    }
    set rc 0
    return
}

#
# PROCEDURE
#
# Declare procedure to obtain the ethernet or mac address property from the    
# ethernet card.
# 
# 3 commands lines containing a total of 6 commands are used.
#
# The get-package-property command is an example of a command
# that takes it's arguments off the stack and puts the results back onto the 
# stack. Because of this, the arguments for the get-package-property command
# are in front of the command verb.
#
#
# The only reason this procedure is implemented in a loop is to avoid coding
# 3 expect commands. 

proc get_mac_addr { phandle } {

    global rc
    global spawn_id_rconsole
    global expect_out
    global command     
    global DSPMSG
    global CATALOG
    global PROGRAM
    global HMCNAME

    set rc 0
    nc_msg "$PROGRAM Status: get_mac_addr start\n"

    # cmd(0) could have been sent as 3 commands. " mac-address" (tcl forces
    # the use of \") is the first command on this line. The result of entering
    # " mac-address" is that 2 stack entries are created, and address and a length.
    #
    # The next command in cmd(0) is the phandle with no quotes. This results in
    # one stack entry because the phandle is an address.
    # 
    # the third command in cmd(0) is get-package-property. After this command, there
    # are 3 stack entries (return code, address and length of mac-address).
    # state 0, stack count 0, send command
    set done(0) 0
    set cmd(0) "\" local-mac-address\" $phandle get-package-property\r"
    set msg(0) "$PROGRAM Status: return code and mac-address now on stack\n"
    set pattern(0) "(.*)3 >(.*)"
    set newstate(0) 1

    # cmd(1) is a dot (.). This is a stack manipulation command that removes one 
    # thing from the stack. pattern(1) is looking for a prompt with the 2 indicating
    # that there are 2 things left on the stack.
    # state 1, return code and mac-address on stack
    set done(1) 0
    set cmd(1) ".\r"
    set msg(1) "$PROGRAM Status: mac-address now on stack\n"
    set pattern(1) "(.*)2 >(.*)"
    set newstate(1) 2

    # cmd(2) is the dump command. This takes an address and a length off the stack
    # and displays the contents of that storage in ascii and hex. The long pattern
    # puts the hex into the variable expect_out(3,string). The tcl verb 'join' is 
    # used to eliminate the spaces put in by the dump command.
    # state 2, mac-address on stack
    set done(2) 0
    set cmd(2) "dump\r"
    set msg(2) "$PROGRAM Status: mac-address displayed, stack empty\n"
    set pattern(2) "(.*)(: )(.*)( :)(.*)(: ok)"
    set newstate(2) 3

    # state 3, all done
    set done(3) 1

    set state 0
    set timeout 60     ;# shouldn't take long
    while { $done($state) == 0 } {
        nc_msg "$PROGRAM Status: command is $cmd($state)\n"
        set command $cmd($state)
        send_command
        expect {
            -i $spawn_id_rconsole
            -re $pattern($state) {
                nc_msg $msg($state)  
                set state $newstate($state)
            }
            "1 > " {
                if { $state == 0 } {
                    # An error occurred while obtaining the mac address.  Log the error,
                    # but don't quit nodecond.  instead, return NA for the address
                    #
                    set command ".\r"
                    send_command
                    expect {
                        -i $spawn_id_rconsole
                        -re "(-*\[0-9\]*)  ok(.*)0 >(.*)" {
                            set mac_rc $expect_out(1,string)
                            nc_msg "$PROGRAM Status: Error getting MAC address for phandle=$phandle. RC=$mac_rc.\n"
                            system "$DSPMSG -s 1 $CATALOG 15 '# Could not obtain MAC address; setting MAC to \"NA\"\n' 2>/dev/null" 
                            set rc 0
                            set mac_address "NA"
                            return $mac_address
                        }
                        timeout {
                            system "$DSPMSG -s 1 $CATALOG 10 '%s: timeout\n' \"$PROGRAM\" 2>/dev/null"
                            nc_msg "timeout state is $state\n"
                            set rc 1
                            return
                        }
                        eof {
                            system "$DSPMSG -s 1 $CATALOG 11 '%s: cannot connect to %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
                            set rc 1
                            return
                        }
                    }
                }
            }
            -re "]" {
                system "$DSPMSG -s 1 $CATALOG 09 '%s: unexpected prompt\n' \"$PROGRAM\" 2>/dev/null"
                set rc 1
                return
            }
            -re "(.*)DEFAULT(.*)" {
                system "$DSPMSG -s 1 $CATALOG 14 '%s: default catch error\n' \"$PROGRAM\" 2>/dev/null"
                set rc 1
                return
            }
            timeout {
                system "$DSPMSG -s 1 $CATALOG 10 '%s: timeout\n' \"$PROGRAM\" 2>/dev/null"
                nc_msg "timeout state is $state\n"
                set rc 1
                return
            }
            eof {
                system "$DSPMSG -s 1 $CATALOG 11 '%s: cannot connect to %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
                set rc 1
                return
            }
        }
    }
    # if the state is 0, 1, or 2, an error occurred and the join will fail
    if { $state == 3 } {
        set mac_address [ join $expect_out(3,string) "" ]
        set rc 0
        return $mac_address
    } else {
        set rc -1
    }
}

#
# PROCEDURE
#
# Declare procedure to obtain the list of ethernet adapters, their physical
# location codes and MAC addresses.
# 
# The get-package-property command is an example of a command
# that takes it's arguments off the stack and puts the results back onto the 
# stack. Because of this, the arguments for the get-package-property command
# are in front of the command verb.
#
# The only reason this procedure is implemented in a loop is to avoid coding
# 3 expect commands. 

proc get_adaptr_loc { phandle } {

    global rc
    global spawn_id_rconsole
    global expect_out
    global command     
    global DSPMSG
    global CATALOG
    global PROGRAM
    global HMCNAME
    global SQUADRON

    set rc 0
    nc_msg "$PROGRAM Status: get_adaptr_loc start\n"

    # cmd(0) could have been sent as 3 commands. " ibm,loc-code" (tcl forces
    # the use of \") is the first command on this line. The result of entering
    # " ibm,loc-code" is that 2 stack entries are created, and address and a length.
    #
    # The next command in cmd(0) is the phandle with no quotes. This results in
    # one stack entry because the phandle is an address.
    # 
    # the third command in cmd(0) is get-package-property. After this command, there
    # are 3 stack entries (return code, address and length of mac-address).
    # state 0, stack count 0, send command
    set done(0) 0
    set cmd(0) "\" ibm,loc-code\" $phandle get-package-property\r"
    set msg(0) "$PROGRAM Status: return code and loc-code now on stack\n"
    set pattern(0) "(.*)3 >(.*)"
    set newstate(0) 1

    # cmd(1) is a dot (.). This is a stack manipulation command that removes one 
    # thing from the stack. pattern(1) is looking for a prompt with the 2 indicating
    # that there are 2 things left on the stack.
    # state 1, return code and loc-code on stack
    set done(1) 0
    set cmd(1) ".\r"
    set msg(1) "$PROGRAM Status: loc-code now on stack\n"
    set pattern(1) "(.*)2 >(.*)"
    set newstate(1) 2

    # state 2, loc-code on stack
    set done(2) 0
    set cmd(2) "dump\r"
    set msg(2) "$PROGRAM Status: loc-code displayed, stack empty\n"
    set pattern(2) "(.*)(: )(.*)( :)(.*)(\.: ok)"
    set newstate(2) 3

    # state 3, all done
    set done(3) 1

    set state 0
    set timeout 60    ;# shouldn't take long
    while { $done($state) == 0 } {
        nc_msg "$PROGRAM Status: command is $cmd($state)\n"
        set command $cmd($state)
        send_command
        expect {
            -i $spawn_id_rconsole
            -re $pattern($state) {
                nc_msg $msg($state)  
                set state $newstate($state)
            }
            "1 > " {
                if { $state == 0 } {
                    set command ".\r"
                    send_command
                    expect {
                        -i $spawn_id_rconsole
                        -re "(-*\[0-9\]*)  ok(.*)0 >(.*)" {
                            set loc_rc $expect_out(1,string)
                            system "$DSPMSG -s 1 $CATALOG 16 '%s: Error getting adapter physical location.\n' \"$PROGRAM\" 2>/dev/null"
                            nc_msg "$PROGRAM Status: Error getting physical location for phandle=$phandle. RC=$loc_rc.\n"
                            set rc 1
                            return
                        }
                        timeout {
                            system "$DSPMSG -s 1 $CATALOG 10 '%s: timeout\n' \"$PROGRAM\" 2>/dev/null"
                            set rc 1
                            return
                        }
                        eof {
                            system "$DSPMSG -s 1 $CATALOG 11 '%s: cannot connect to %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
                            set rc 1
                            return
                        }
                    }
                }
            }
            -re "]" {
                system "$DSPMSG -s 1 $CATALOG 09 '%s: unexpected prompt\n' \"$PROGRAM\" 2>/dev/null"
                set rc 1
                return
            }
            -re "(.*)DEFAULT(.*)" {
                system "$DSPMSG -s 1 $CATALOG 14 '%s: default catch error\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
                set rc 1
                return
            }
            timeout {
                system "$DSPMSG -s 1 $CATALOG 10 '%s: timeout\n' \"$PROGRAM\" 2>/dev/null"
                nc_msg "$PROGRAM Status: timeout state is $state\n"
                set rc 1
                return
            }
            eof {
                system "$DSPMSG -s 1 $CATALOG 11 '%s: cannot connect to %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
                set rc 1
                return
            }
        }
    }
    if { $state == 3 } {
        set rc 0

        if { $SQUADRON &&
             [regexp "(.*)(: )(.*)( :)(.*)(:)" $expect_out(1,string) v1 v2 v3 v4 v5 v6 ] } {
            return $v6$expect_out(5,string)
        } else {
            return $expect_out(5,string)
        }
    } else {
        set rc -1
    }
}

#
# PROCEDURE
#
# Declare procedure to ping the given server through the specified LAN adapter.
# The procedure will return 0 if the ping is successful, and 1 if the ping
# was unsuccessful.  The global rc variable will be set if there's an error.
#

proc ping_server { full_path_name phandle } {

    global spawn_id_rconsole
    global expect_out
    global server_ip
    global client_ip
    global gateway_ip
    global list_type
    global adap_speed
    global adap_duplex
    global command
    global adap_prop_list
    global rc
    global DSPMSG
    global CATALOG
    global PROGRAM
    global HMCNAME

    nc_msg "$PROGRAM Status: ping_server start\n"

    set j 0
    set properties_matched 0
    set adap_conn ""
    set speed_list {}
    set duplex_list {}
    set adap_conn_list {}

    # If the adapter type chosen is ethernet, need to set the speed and duplex
    # of the adapter before we perform the ping.  If token ring or fddi,
    # this is not required, so begin with state 2.
    # 
    # cmd(0) sets the given adapter as active, to allow setting of speed
    # and duplex
    #
    # cmd(1) writes the settings to the current adapter
    #
    # cmd(2) selects the /packages/net node as the active package to access the
    # ping command.
    #
    # The next command in cmd(3) is the ping command. This places the return code
    # on the stack. A return code of 0 indicates success.
    #
    # state 0, set the current adapter
    set done(0) 0
    set cmd(0) "dev $full_path_name\r"
    set msg(0) "$PROGRAM Status: selected $full_path_name as the active adapter\n"
    set pattern(0) "(.*)0 >(.*)"
    set newstate(0) 1

    # state 1, send property command to set selected type
    set done(1) 0
    set cmd(1) "\" ethernet,$adap_speed,$adap_conn,$adap_duplex\" encode-string \" chosen-network-type\" property\r"
    set msg(1) "$PROGRAM Status: chosen network type set\n"
    set pattern(1) "(.*)0 >(.*)"
    set newstate(1) 2

    # state 2, activate /packages/net
    set done(2) 0
    set cmd(2) "dev /packages/net\r"
    set msg(2) "$PROGRAM Status: selected the /packages/net node as the active package\n"
    set pattern(2) "(.*)ok(.*)0 >(.*)"
    set newstate(2) 3

    # state 3, ping the server
    set done(3) 0
    set cmd(3) "\" $full_path_name:$client_ip,$server_ip,$gateway_ip\" tty-do-ping\r"
    set msg(3) "$PROGRAM Status: ping return code now on stack\n"
    set pattern(3) "(.*)ok(.*)(\[1-2\]) >(.*)"
    set newstate(3) 4

    # state 4, get the return code off the stack
    set done(4) 0
    set cmd(4) ".\r"
    set msg(4) "$PROGRAM Status: return code displayed, stack empty\n"
    set pattern(4) "(\[0-9\]*)  ok(.*)(\[0-1\]) >(.*)"
    set newstate(4) 5

    # this command is used to work around a default catch problem in open
    # firmware.  Without it, a default catch occurs if we try to set
    # adapter properties again after a ping
    #
    # state 5, reset pointer         
    set done(5) 0
    set cmd(5) "0 to my-self\r"
    set msg(5) "$PROGRAM Status: resetting pointer\n"
    set pattern(5) "(.*)ok(.*)0 >(.*)"
    set newstate(5) 6

    # state 6, all done
    set done(6) 1

    # for ping, only need to set speed and duplex for ethernet adapters
    #
    if { $list_type == "ent" } {
        set state 0

        # Get the list of properties for this adapter
        #
        get_adap_prop $phandle
        if { $rc != 0 } {
            exit 1
        }

        if { [ llength $adap_prop_list ] == 0 } {
            system "$DSPMSG -s 1 $CATALOG 20 '%s: no properties found for adapter %s\n' \"$PROGRAM\" \"$full_path_name\" 2>/dev/null"
            set rc 0
            return 1
        }

        # Now need to verify that the network params we were passed are valid for
        # the given adapter
        #
        foreach prop $adap_prop_list {
            regexp (.*),(.*),(.*) $prop dummy a_speed a_conn a_duplex
            if { ( $a_speed == $adap_speed ) && ( $a_duplex == $adap_duplex ) } {
                set properties_matched 1
                if { [ lsearch $adap_conn_list $a_conn ] == -1 } {
                    lappend adap_conn_list $a_conn
                }
            }
        }

        set i [ llength $adap_conn_list ]

        if { $properties_matched == 0 } {
            system "$DSPMSG -s 1 $CATALOG 21 '%s: %s/%s settings are not supported on this adapter\n' \"$PROGRAM\" \"$adap_speed\" \"$adap_duplex\" 2>/dev/null"
            set rc 0
            return 1
        }
    } else {
        set state 2
    }

    set timeout 300
    while { $done($state) == 0 } {
        nc_msg "$PROGRAM Status: command is $cmd($state)\n"
        set command $cmd($state)

        send_command

        expect {
            -i $spawn_id_rconsole
            -re $pattern($state) {
                nc_msg $msg($state)
                set state $newstate($state)

                if { $state == 1 } {
                    set adap_conn [ lindex $adap_conn_list $j ]
                    set cmd(1) "\" ethernet,$adap_speed,$adap_conn,$adap_duplex\" encode-string \" chosen-network-type\" property\r"
                    nc_msg "$PROGRAM Status: Trying connector type $adap_conn\n"
                    incr j 1
                }

                if { $state == 4 } {
                    set ping_debug $expect_out(buffer)
                }
 
                if { $state == 5 } {
                    set ping_debug_result $expect_out(buffer)
                    set ping_rc $expect_out(1,string)
                    set stack_level $expect_out(3,string)
                    # nc_msg "$PROGRAM Status: expect_out is <$expect_out(buffer)>\n"
                    # nc_msg "$PROGRAM Status: ping rc is <$ping_rc>\n"
                    # nc_msg "$PROGRAM Status: stack level is <$stack_level>\n"
                    if { $ping_rc == 0 } {
                        system "$DSPMSG -s 1 $CATALOG 74 '# %s ping successful.\n' \"$full_path_name\" 2>/dev/null"
                    } elseif { $ping_rc == 1 } {
                        system "$DSPMSG -s 1 $CATALOG 75 '# %s ping unsuccessful\n' \"$full_path_name\" 2>/dev/null"
                        nc_msg $ping_debug
                        nc_msg "$ping_debug_result\n"

                        # An unsuccessful return may leave another item on the stack to 
                        # be removed.  Check for it, and remove if necessary
                        while { $stack_level != 0 } {
                            set command ".\r"
                            send_command
                            expect {
                                -i $spawn_id_rconsole
                                -re "(\[0-9\]*)  ok(.*)(\[0-1\]) >(.*)" {
                                    set stack_level $expect_out(3,string)
                                    nc_msg "$PROGRAM Status: stack_level is <$stack_level>\n"
                                }
                                -re "]" {
                                    system "$DSPMSG -s 1 $CATALOG 09 '%s: unexpected prompt\n' \"$PROGRAM\" 2>/dev/null"
                                    set rc 1
                                    return
                                }
                                -re "(.*)DEFAULT(.*)" {
                                    system "$DSPMSG -s 1 $CATALOG 14 '%s: default catch error\n' \"$PROGRAM\" 2>/dev/null"
                                    set 1
                                    return
                                }
                                timeout {
                                    system "$DSPMSG -s 1 $CATALOG 10 '%s: timeout\n' \"$PROGRAM\" 2>/dev/null"
                                    set 1
                                    return
                                }
                                eof {
                                    system "$DSPMSG -s 1 $CATALOG 11 '%s: cannot connect to %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
                                    set 1
                                    return
                                }
                            }
                        }
                        # Check if there are any more adapter connector types
                        # to try
                        #
                        if { ( $list_type == "ent" ) && ( $j < $i ) } {
                            set adap_conn [ lindex $adap_conn_list $j ]
                            nc_msg "$PROGRAM Status: Trying connector type $adap_conn\n"
                            incr j 1

                            # Need to work around a default catch problem in open
                            # firmware by sending a "0 to my-self" instruction
                            # following the ping.  To make sure this happens in
                            # this rare case where we have an adapter with multiple connectors,
                            # we have to force the instruction into the 0th slot in
                            # the array.  This is OK, since we only set the current
                            # adapter once, upon entering this procedure.
                            #
                            set done(0) 0
                            set cmd(0) "0 to my-self\r"
                            set msg(0) "$PROGRAM Status: resetting pointer\n"
                            set pattern(0) "(.*)ok(.*)0 >(.*)"
                            set newstate(0) 1
                            
                            set state 0
                        }
                    } else {
                        system "$DSPMSG -s 1 $CATALOG 24 '%s: unexpected ping return code\n' \"$PROGRAM\" 2>/dev/null"
                        set rc 1
                        return
                    }
                }
            }
            -re "]" {
                system "$DSPMSG -s 1 $CATALOG 09 '%s: unexpected prompt\n' \"$PROGRAM\" 2>/dev/null"
                set rc 1
                return
            }
            -re "(.*)DEFAULT(.*)" {
                system "$DSPMSG -s 1 $CATALOG 14 '%s: default catch error\n' \"$PROGRAM\" 2>/dev/null"
                set rc 1
                return
            }
            timeout {
                system "$DSPMSG -s 1 $CATALOG 10 '%s: timeout\n' \"$PROGRAM\" 2>/dev/null"
                set rc 1
                return
            }
            eof {
                system "$DSPMSG -s 1 $CATALOG 11 '%s: cannot connect to %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
                set rc 1
                return
            }
        }
    }
    set rc 0
    return $ping_rc
}


###################################################################
#
# PROCEDURE
#
# Declare procedure to boot the system from the selected ethernet card.    
# 
# This routine does the following:
# 1. Initiates the boot across the network. (state 0 or 1)
# 
# state 99 is normal exit and state -1 is error exit.
###################################################################
proc boot_network {} {
    global rc
    global spawn_id_rconsole
    global full_path_name
    global command
    global speed
    global duplex
    global chosen_adap_type
    global server_ip
    global client_ip
    global gateway_ip
    global extra_args
    global CATALOG
    global DSPMSG
    global PROGRAM
    global HMCNAME
    set rc 0
    set i 0

    nc_msg "$PROGRAM Status: boot_network start\n"

    ###################################################################
    # Variables associated with each of the commands sent by this routine
    # are defined below.
    #
    # The done variable is flag that is set to 1 to break out of the loop
    #
    # The cmd variable is the command to be sent to the chrp interface.
    #     In one case it set in the special processing code because the 
    #     ihandle is not available then this code is executes.
    #
    # The msg variable contains the message sent after a successful pattern match
    #
    # The pattern variable is the pattern passed to expect 
    #
    # The newstate variable indicates what command is to be issued next
    ###################################################################

    # If the install adapter is Ethernet or Token Ring, set the speed and 
    # duplex during boot.
    # state 0, stack count 0
    set done(0) 0
#   set cmd(0) "boot $full_path_name:speed=$speed,duplex=$duplex,bootp,0.0.0.0,,0.0.0.0,0.0.0.0\r"
    set cmd(0) "boot $full_path_name:speed=$speed,duplex=$duplex,bootp,$server_ip,,$client_ip,$gateway_ip $extra_args\r"
    set msg(0) "$PROGRAM Status: network boot initiated\n"
    set pattern(0) "BOOTP"
    set newstate(0) 99

    # If the install adapter is FDDI, don't set the speed and duplex
    # state 1
    set done(1) 0
#   set cmd(1) "boot $full_path_name:bootp,0.0.0.0,,0.0.0.0,0.0.0.0\r"
    set cmd(1) "boot $full_path_name:bootp,$server_ip,,$client_ip,$gateway_ip\r"
    set msg(1) "$PROGRAM Status: network boot initiated\n"
    set pattern(1) "BOOTP"
    set newstate(1) 99

    # state 99, all done
    set done(99) 1

    # state -1, all done
    set done(-1) 1

    ##############################################################################
    # This is the expect code.        
    # First, the initial state is set to 0.
    # Then, in a loop, 
    #    the done flag is checked,
    #    the command is sent to the chrp interface
    #    expect listens with the pattern 
    #    
    ##############################################################################
    if { $chosen_adap_type ==  "fddi" } {
        set state 1
    } else {
        if { $speed == "" || $duplex == "" } {
            system "$DSPMSG -s 1 $CATALOG 25 '%s: cannot set speed or duplex for network boot\n' \"$PROGRAM\" 2>/dev/null"
            set rc 1
            return
        }
        set state 0
    }

    set timeout 30     ;# shouldn't take long
    while { $done($state) == 0 } {
        nc_msg "$PROGRAM Status: command is $cmd($state)\n"
        set command $cmd($state)
        send_command
        expect {
            -i $spawn_id_rconsole
            -re $pattern($state) {
                nc_msg $msg($state)
                set state $newstate($state)
            }
            -re "]" {
                system "$DSPMSG -s 1 $CATALOG 09 '%s: unexpected prompt\n' \"$PROGRAM\" 2>/dev/null"
                set rc 1
                return
            }
            -re "(.*)DEFAULT(.*)" {
                system "$DSPMSG -s 1 $CATALOG 14 '%s: default catch error\n' \"$PROGRAM\" 2>/dev/null"
                set rc 1
                return
            }
            timeout {
                system "$DSPMSG -s 1 $CATALOG 10 '%s: timeout\n' \"$PROGRAM\" 2>/dev/null"
                nc_msg "timeout state is $state\n"
                set rc 1
                return
            }
            eof {
                system "$DSPMSG -s 1 $CATALOG 11 '%s: cannot connect to %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
                set rc 1
                return
            }
        }
    }
}

#
# PROCEDURE
#
#
proc Boot {} {
    global rc
    global expect_out
    global spawn_id_rconsole
    global CATALOG
    global DSPMSG
    global PROGRAM
    global HMCNAME

    set rc 0

    nc_msg "$PROGRAM Status: waiting for the boot image to boot up.\n"

    set timeout 1200      ;# could take a while depending on configuration

    # check for bootp response error
    expect {
        -i $spawn_id_rconsole \
        -re {!([0-9A-F]+)} {
            system "$DSPMSG -s 1 $CATALOG 26 '%s: The network boot ended in an error.\n' \"$PROGRAM\" 2>/dev/null"
            nc_msg $expect_out(buffer)
            set rc 1
            return
        }
        -ex {--------------------------------} {
            system "$DSPMSG -s 1 $CATALOG 27 '# Network boot proceeding, %s is exiting.\n' \"$PROGRAM\" 2>/dev/null"
        }
        timeout {
            set mins [expr ($timeout/60)]
            system "$DSPMSG -s 1 $CATALOG 59 \
                           '%s: timeout waiting for the boot image to boot up. \
                           \n%s waited %s minutes for the boot image to boot. \
                           \nEither the boot up has taken longer than expected or \
                           \nthere is a problem with system boot.  Check the boot \
                           \nof the node to determine if there is a problem.\n' \
                           \"$PROGRAM\" \"$PROGRAM\" \"$mins\" \
                           2>/dev/null"
            nc_msg $expect_out(buffer)
            set rc 1
            return
        }                                                                       
        eof {                                                                   
            system "$DSPMSG -s 1 $CATALOG 60 '%s: Port closed waiting for boot image to boot.\n' \"$PROGRAM\" 2>/dev/null"                                       
            set rc 1                                                            
            return                                                              
        }                                                                       
    }
}

#
# Signal handling stuff
#
trap {
    global DSPMSG
    global CATALOG
    global PROGRAM

    set signal SIG[trap -name]

    system "$DSPMSG -s 1 $CATALOG 30 '%s: received signal named %s\n' \"$PROGRAM\" \"$signal\" 2>/dev/null"
    exit 3
} {INT HUP QUIT TERM}

#
# Main MAIN main
#
set BINPATH "/opt/hsc/bin"
set DSPMSG "/usr/bin/dspmsg"
set PROGRAM "lpar_netboot"
set CATALOG "/usr/share/locale/C/LC_MESSAGES/IBMhsc.netboot.cat"
set SQUADRON 0
set TYPE_FULL 0

# Determine the location of the message catalog
if {[info exists env(LANG)]} {
    set LANG $env(LANG)
    set CATALOG "/usr/share/locale/$LANG/LC_MESSAGES/IBMhsc.netboot.cat"
}

set noboot 0        ;# default is to boot
set Boot_timeout 3000

global expect_out
global loc_code

# Flags and initial variable
set discovery 0
set discover_all 0
set hdr_printed 0
set verbose 0
set discover_macaddr 0
set rc 0
set debug_flag 0
set rmvterm_flag 0
set full_path_name ""
set adap_speed ""
set adap_duplex ""
set client_ip ""
set server_ip ""
set gateway_ip ""
set extra_args ""
set macaddress ""
set phys_loc ""

# List supported network adapters here.  dev_pat is an array of regexp patterns
# the script searches for in the device tree listing.  dev_type is the type
# of device displayed in the output.
set dev_pat(0) "ethernet"
set dev_type(0) "ent"
set dev_pat(1) "token-ring"
set dev_type(1) "tok"
set dev_pat(2) "fddi"
set dev_type(2) "fddi"

set dev_count [array size dev_pat]

#
# Log the process id
#
set proc_id [ eval pid ]
nc_msg "$PROGRAM Status: process id is $proc_id\n"

#
#
# Process command line options and parameters
#
#
while { [llength $argv] > 0} {
    set flag [lindex $argv 0]
    switch -glob -- $flag {

        "-n" { set noboot 1
               set argv [lrange $argv 1 end]
             } 

        "-t" { set list_type [lindex $argv 1]
               set argv [lrange $argv 2 end]

               #
               # Validate the argument
               #
               set dev_type_found 0
               foreach dev [array names dev_type] {
                   if {$dev_type($dev) == $list_type} {
                       set dev_type_found 1
                       break
                   }
               }

               if {$dev_type_found == 0 } {
                   system "$DSPMSG -s 1 $CATALOG 31 '%s: %s is not a valid adapter choice\n' \"$PROGRAM\" \"$list_type\" 2>/dev/null"
                   exit 1
               }
             }

        "-D" { set discovery 1
               set argv [lrange $argv 1 end]
             }

        "-A" { set discover_all 1
               set argv [lrange $argv 1 end]
             }

        "-s" { set adap_speed [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-d" { set adap_duplex [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-S" { set server_ip [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-C" { set client_ip [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-G" { set gateway_ip [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-v" { set verbose 1
               set argv [lrange $argv 1 end]
             }

	"-M" { set discover_macaddr 1
	       set argv [lrange $argv 1 end]
	     }

        "-m" { set macaddress [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

        "-l" { set phys_loc [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }
	
	"-g" { set extra_args [lindex $argv 1]
               set argv [lrange $argv 2 end]
             }

	"-x" { set debug_flag 1
               set argv [lrange $argv 1 end]
             }

        "-f" { set rmvterm_flag 1
               set argv [lrange $argv 1 end]
             }

        "-*" { system "$DSPMSG -s 1 $CATALOG 32 '%s: illegal option: %s\n' \"$PROGRAM\" \"$flag\" 2>/dev/null"
               usage
             }
        default {break}
    }
}

if { [llength $argv] < 1 } {
    system "$DSPMSG -s 1 $CATALOG 33 '%s: missing parameter\n' \"$PROGRAM\" 2>/dev/null"
    usage
}

ck_plat
set arg0 [lindex $argv 0]
set arg1 [lindex $argv 1]

#              
# FullSystemPartition does not require profile so we check to see
# if arg1 and arg2 are the same.  If the arg1 and arg2 are the same
# then that will signify a FullSystemPartition.
#
if { !$SQUADRON && [ string compare "$arg0" "$arg1"] == 0 } {
    set rc [catch {eval exec $BINPATH/lssyscfg -r sys -m $arg0 -n $arg1 -Fname} msg]

    if { $rc } {
        if { [llength $argv] > 3 } {
            system "$DSPMSG -s 1 $CATALOG 34 '%s: extraneous parameter(s)\n' \"$PROGRAM\" 2>/dev/null"
            usage
        } else {
           set node [lindex $argv 0]
           set profile [lindex $argv 1]
           set manage [lindex $argv 2]
        }
    } elseif { [string compare "$msg" "$arg0"] == 0 } {
        set TYPE_FULL 1
        if { [llength $argv] > 2 } {
            system "$DSPMSG -s 1 $CATALOG 34 '%s: extraneous parameter(s)\n' \"$PROGRAM\" 2>/dev/null"
            usage
        }

        set node [lindex $argv 0]
        set manage [lindex $argv 1]
        set profile ""
    } else {
        system "$DSPMSG -s 1 $CATALOG 44 '%s: Machine state query failure detected\n' \"$PROGRAM\" 2>/dev/null"
        nc_msg "$PROGRAM Status: error from lssyscfg command\n"
        system "$DSPMSG -s 1 $CATALOG 39 '%s: Error : %s\n' \"$PROGRAM\" \"$msg\" 2>/dev/null"
        exit 1
    }
} else {
    if { [llength $argv] > 3 } {
        system "$DSPMSG -s 1 $CATALOG 34 '%s: extraneous parameter(s)\n' \"$PROGRAM\" 2>/dev/null"
        usage
    }

    set node [lindex $argv 0]
    set profile [lindex $argv 1]
    set manage [lindex $argv 2]
}

if {$dev_type_found}    { nc_msg "$PROGRAM Status: List only $list_type adapters\n"           }
if {$noboot}            { nc_msg "$PROGRAM Status: -n (no boot) flag detected\n"              }
if {$discovery}         { nc_msg "$PROGRAM Status: -D (discovery) flag detected\n"            }
if {$discover_all}      { nc_msg "$PROGRAM Status: -A (discover all) flag detected\n"         }
if {$verbose}           { nc_msg "$PROGRAM Status: -v (verbose debug) flag detected\n"        }
if {$discover_macaddr}  { nc_msg "$PROGRAM Status: -M (discover mac address) flag detected\n" }

if {$debug_flag}        { 
    nc_msg "$PROGRAM Status: -d (debug) flag detected\n" 
    log_user 1
}

if {!$discover_macaddr && $adap_speed != ""}  { nc_msg "$PROGRAM Status: using adapter speed of $adap_speed\n"        }
if {!$discover_macaddr && $adap_duplex != ""} { nc_msg "$PROGRAM Status: using adapter duplex of $adap_duplex\n"      }
if {!$discover_macaddr && $server_ip != ""}   { nc_msg "$PROGRAM Status: using server IP address of $server_ip\n"     }
if {!$discover_macaddr && $client_ip != ""}   { nc_msg "$PROGRAM Status: using client IP address of $client_ip\n"     }
if {!$discover_macaddr && $gateway_ip != ""}  { nc_msg "$PROGRAM Status: using gateway IP address of $gateway_ip\n"   }
if {!$discover_macaddr && $macaddress != ""}  { nc_msg "$PROGRAM Status: using macaddress of $macaddress\n"           }
if {!$discover_macaddr && $phys_loc != ""}    { nc_msg "$PROGRAM Status: using physical location code of $phys_loc\n" }
set HMCNAME $node

ck_args

if {$extra_args != "" }  { nc_msg "$PROGRAM Status: extra arguments sent to booting: $extra_args\n" }

if { ! $noboot } {
    set hdw_addr ""
    set speed $adap_speed
    set duplex $adap_duplex
}

if {$rmvterm_flag} {
    nc_msg "$PROGRAM Status: -f (force close virtual session) flag detected\n"
    if { $TYPE_FULL && !$SQUADRON } {
        set rc [catch {eval exec $BINPATH/rmvterm -m $manage} msg]
    } else {
        set rc [catch {eval exec $BINPATH/rmvterm -m $manage -p $node} msg]
    }
}

#
# open the S1 serial port
#
nc_msg "$PROGRAM Status: open port\n"

#
# Assume conserver is running, and invoke rconsole with the -f flag
# to force a read-write session.  If conserver is not available,
# we'll re-try without it later
#
if { $TYPE_FULL && !$SQUADRON } {
    set pid_rconsole [eval spawn -nottycopy -nottyinit \
                      $BINPATH/mkvterm -m $manage]
} else {
    set pid_rconsole [eval spawn -nottycopy -nottyinit \
                      $BINPATH/mkvterm -m $manage -p $node]
}
set spawn_id_rconsole $spawn_id
nc_msg "$PROGRAM Status: spawn_id is $spawn_id\n"

#
#
# insure we got the serial connection before we power off the node.
# what we don't want is eof. this means that we didn't get the
# connection. we will accept a timeout. this means that we timed 
# out waiting for nothing, but we didn't get eof.  Must also check
# for successful execution of rconsole.  Note that the spawn will
# be successful even if rconsole fails.  Check the next expect
# command to see if there's an rconsole error message present -
# if so, log it and exit.
#
set timeout 120

set done 0
while { ! $done } {
    expect {
        -i $spawn_id_rconsole \

        "NVTS* 9734 *_VT_" {
	    if { !$discover_macaddr || ( $discover_macaddr && $verbose ) } {
                system "$DSPMSG -s 1 $CATALOG 35 '# Connecting to %s.\n' \"$HMCNAME\" 2>/dev/null"
            }
            set done 1
        }
	"Open in progress.." {
            if { !$discover_macaddr || ( $discover_macaddr && $verbose ) } {
                system "$DSPMSG -s 1 $CATALOG 35 '# Connecting to %s.\n' \"$HMCNAME\" 2>/dev/null"
            }
	    set done 1
	}
        timeout { 
            system "$DSPMSG -s 1 $CATALOG 10 '%s: timeout\n' \"$PROGRAM\" 2>/dev/null" 
            set rc 1
            return
        }
        eof {
            system "$DSPMSG -s 1 $CATALOG 11 '%s: cannot connect to %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"

            exit 1
        }
    }
}

expect -i $spawn_id_rconsole -re "ok" {  
    if { !$discover_macaddr || ( $discover_macaddr && $verbose ) } {
        system "$DSPMSG -s 1 $CATALOG 36 '# Connected\n' 2>/dev/null" 
    }
}  

set timeout $Boot_timeout;   # restore timeout

if { !$discover_macaddr || ( $discover_macaddr && $verbose ) } {
    system "$DSPMSG -s 1 $CATALOG 37 '# Checking for power off.\n' 2>/dev/null"
}

if { !$SQUADRON && $TYPE_FULL } {
    set rc [catch {eval exec $BINPATH/lssyscfg -r sys -n $manage -Fstate} msg]
} elseif { !$SQUADRON && !$TYPE_FULL } {
    set rc [catch {eval exec $BINPATH/lssyscfg -r lpar -m $manage -n $node -Fstate} msg]
} elseif { $SQUADRON && !$TYPE_FULL } {
    set rc [catch {eval exec $BINPATH/lssyscfg -r lpar -m $manage --filter lpar_names=$node -Fstate} msg]
}

if { $rc } {
    system "$DSPMSG -s 1 $CATALOG 38 '%s: Unable to determine machine state\n' \"$PROGRAM\" 2>/dev/null"
    nc_msg "$PROGRAM Status: error from lssyscfg command\n"
    system "$DSPMSG -s 1 $CATALOG 39 '%s: Error : %s\n' \"$PROGRAM\" \"$msg\" 2>/dev/null"
    exit 1
}

if { ( !$SQUADRON && $TYPE_FULL && ([string compare "$msg" "No Power"] == 0) ) || 
     ( !$SQUADRON && !$TYPE_FULL && ([string compare "$msg" "Ready"] == 0) ) || 
     ( $SQUADRON && !$TYPE_FULL && 
       ( ([string compare "$msg" "Off"] == 0) || 
         ([string compare "$msg" "Not Activated"] == 0) ) ) } {
    if { !$discover_macaddr || ( $discover_macaddr && $verbose ) } {
        system "$DSPMSG -s 1 $CATALOG 40 '# Power off complete.\n' 2>/dev/null"
    }
} else { 

    #
    #
    # power off the node
    #
    if { !$discover_macaddr || ( $discover_macaddr && $verbose ) } {
        system "$DSPMSG -s 1 $CATALOG 41 '# Power off the node.\n' 2>/dev/null"
    }

    if { !$SQUADRON && $TYPE_FULL } {
        set rc [catch {eval exec $BINPATH/chsysstate -r sys -o off -n $manage} msg]
    } elseif { !$SQUADRON && !$TYPE_FULL } {
        set rc [catch {eval exec $BINPATH/chsysstate -r lpar -o off -m $manage -n $node} msg]
    } elseif { $SQUADRON && !$TYPE_FULL } {
        set rc [catch {eval exec $BINPATH/chsysstate -r lpar -o shutdown -m $manage -n $node} msg]
    }


    if { $rc } {
        system "$DSPMSG -s 1 $CATALOG 42 '%s: cannot power off %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
        nc_msg "$PROGRAM Status: error from chsysstate command\n"
        system "$DSPMSG -s 1 $CATALOG 39 '%s: Error : %s\n' \"$PROGRAM\" \"$msg\" 2>/dev/null"
        exit 1
    }

    if { !$discover_macaddr || ( $discover_macaddr && $verbose ) } {
        system "$DSPMSG -s 1 $CATALOG 43 '# Wait for power off.\n' 2>/dev/null"
    }

    #
    #   
    # chsysstate will return a successful result if it was able to pass the power
    # command from the HMC to the node, but the node may not be powered off yet.  Need
    # to query to make sure the node is off before we continue
    #   
    set done 0
    set query_count 0
    while { ! $done } {
        if { !$SQUADRON && $TYPE_FULL } { 
            set rc [catch {eval exec $BINPATH/lssyscfg -r sys -n $manage -Fstate} msg]
        } elseif { !$SQUADRON && !$TYPE_FULL } {
            set rc [catch {eval exec $BINPATH/lssyscfg -r lpar -m $manage -n $node -Fstate} msg]
        } elseif { $SQUADRON && !$TYPE_FULL } {
            set rc [catch {eval exec $BINPATH/lssyscfg -r lpar -m $manage --filter lpar_names=$node -Fstate} msg]           
        }

        if { $rc } {
            system "$DSPMSG -s 1 $CATALOG 44 '%s: Machine state query failure detected\n' \"$PROGRAM\" 2>/dev/null"
            nc_msg "$PROGRAM Status: error from lssyscfg command\n"
            system "$DSPMSG -s 1 $CATALOG 39 '%s: Error : %s\n' \"$PROGRAM\" \"$msg\" 2>/dev/null"
            exit 1
        }
   
        #
        # separate the nodename from the query status
        #
        if { ( !$SQUADRON && $TYPE_FULL && ([string compare "$msg" "No Power"] == 0) ) ||
             ( !$SQUADRON && !$TYPE_FULL && ([string compare "$msg" "Ready"] == 0) ) ||
             ( $SQUADRON && !$TYPE_FULL &&
               ( ([string compare "$msg" "Off"] == 0) ||
               ([string compare "$msg" "Not Activated"] == 0) ) ) } {
            if { !$discover_macaddr || ( $discover_macaddr && $verbose ) } {
                system "$DSPMSG -s 1 $CATALOG 40 '# Power off complete.\n' 2>/dev/null"
            }
	    set done 1
            continue
        }

        incr query_count 
        if { $query_count > 300 } {
            system "$DSPMSG -s 1 $CATALOG 45 '%s: Timed out waiting for power off of %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
            nc_msg "$PROGRAM Status: error from lssyscfg command : $msg\n"
            exit 1
        }

        sleep 1
    }
}

# power on the node
#
if { !$SQUADRON && $TYPE_FULL } {
    set rc [catch {eval exec $BINPATH/lssyscfg -r sys -n $manage -Fstate} msg]
} elseif { !$SQUADRON && !$TYPE_FULL } {
    set rc [catch {eval exec $BINPATH/lssyscfg -r lpar -m $manage -n $node -Fstate} msg]
} elseif { $SQUADRON && !$TYPE_FULL } {
    set rc [catch {eval exec $BINPATH/lssyscfg -r lpar -m $manage --filter lpar_names=$node -Fstate} msg]
}

if { $rc } {
    system "$DSPMSG -s 1 $CATALOG 44 '%s: Machine state query failure detected\n' \"$PROGRAM\" 2>/dev/null"
    nc_msg "$PROGRAM Status: error from lssyscfg command\n"
    system "$DSPMSG -s 1 $CATALOG 39 '%s: Error : %s\n' \"$PROGRAM\" \"$msg\" 2>/dev/null"
    exit 1
}

if { ( !$SQUADRON && $TYPE_FULL && ([string compare "$msg" "No Power"] == 0) ) ||
     ( !$SQUADRON && !$TYPE_FULL && ([string compare "$msg" "Ready"] == 0) ) ||
     ( $SQUADRON && !$TYPE_FULL && 
       ( ([string compare "$msg" "Off"] == 0) || 
         ([string compare "$msg" "Not Activated"] == 0) ) ) } {
    if { !$discover_macaddr || ( $discover_macaddr && $verbose ) } {
        system "$DSPMSG -s 1 $CATALOG 46 '# Power on %s to Open Firmware.\n' \"$HMCNAME\" 2>/dev/null"   
    }

    if { !$SQUADRON && $TYPE_FULL } {
        set rc [catch {eval exec $BINPATH/chsysstate -r sys -o on -b of -c full -n $manage } msg]
	nc_msg "$PROGRAM Status: FullSystemPartition powering on ...\n"
    } elseif { !$SQUADRON && !$TYPE_FULL } {
        set rc [catch {eval exec $BINPATH/chsysstate -r lpar -o on -b of -m $manage -n $node -f $profile} msg]
    } elseif { $SQUADRON && !$TYPE_FULL } {
        set rc [catch {eval exec $BINPATH/chsysstate -r lpar -o on -b of -m $manage -n $node -f $profile} msg]
    }
    nc_msg "$PROGRAM Status: wait for power on\n"
}

if { $rc } {
    system "$DSPMSG -s 1 $CATALOG 47 '%s: cannot power on %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
    nc_msg "$PROGRAM Status: error from chsysstate command\n"
    system "$DSPMSG -s 1 $CATALOG 39 '%s: Error : %s\n' \"$PROGRAM\" \"$msg\" 2>/dev/null"
    exit 1
}

#
# Determine if system is power on as a full system partition.  
# Since full system doesn't indicate the Open Firmware state.  
# Check the op_panel_status to determine if when the system
# is booting to open firmware.
#
if { !$SQUADRON && $TYPE_FULL } {
    set done 0
    set query_count 0
    while { ! $done } {
        set rc [catch {eval exec $BINPATH/lssyscfg -r sys -n $manage -Fop_panel_value} msg]
        if { $rc } {
            system "$DSPMSG -s 1 $CATALOG 44 '%s: Machine state query failure detected\n' \"$PROGRAM\" 2>/dev/null"
            nc_msg "$PROGRAM Status: error from lssyscfg command\n"
            system "$DSPMSG -s 1 $CATALOG 39 '%s: Error : %s\n' \"$PROGRAM\" \"$msg\" 2>/dev/null"
            exit 1
        }

        if { [string compare "$msg" "E1A8"] == 0 } {
            if { !$discover_macaddr || ( $discover_macaddr && $verbose ) } {
                system "$DSPMSG -s 1 $CATALOG 57 '# FullSystemPartition powering on to Open Firmware.\n' 2>/dev/null"
            }
            set done 1
            continue
        }

        if { $query_count > 500 } {
            system "$DSPMSG -s 1 $CATALOG 49 '%s: Timed out waiting for power on of %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
            nc_msg "$PROGRAM Status: error from lssyscfg command : $msg\n"
            exit 1
        }
    }

    sleep 1
}

#
# Verify node is powered on before continuing
#
set done 0
set query_count 0
while { ! $done } {
    if { !$SQUADRON && $TYPE_FULL } {
        set rc [catch {eval exec $BINPATH/lssyscfg -r sys -n $manage -Fstate} msg]
    } elseif { !$SQUADRON && !$TYPE_FULL } {
        set rc [catch {eval exec $BINPATH/lssyscfg -r lpar -m $manage -n $node -Fstate} msg]
    } elseif { $SQUADRON && !$TYPE_FULL } {
        set rc [catch {eval exec $BINPATH/lssyscfg -r lpar -m $manage --filter lpar_names=$node -Fstate} msg]
    }

    if { $rc } {
        system "$DSPMSG -s 1 $CATALOG 44 '%s: Machine state query failure detected\n' \"$PROGRAM\" 2>/dev/null"
        nc_msg "$PROGRAM Status: error from lssyscfg command\n"
        system "$DSPMSG -s 1 $CATALOG 39 '%s: Error : %s\n' \"$PROGRAM\" \"$msg\" 2>/dev/null"
        exit 1
    }

    #
    # separate the nodename from the query status
    #
    if { ( !$SQUADRON && $TYPE_FULL && ([string compare "$msg" "Initializing"] == 0) ) ||
         ( !$TYPE_FULL && ( ([string compare "$msg" "Open Firmware"] == 0) || 
                            ([string compare "$msg" "Open firmware"] == 0) ) ) } {
	if { !$discover_macaddr || ( $discover_macaddr && $verbose ) } {
            system "$DSPMSG -s 1 $CATALOG 48 '# Power on complete.\n' 2>/dev/null"
        }
        set done 1
        continue
    }
    incr query_count
 
    if { $query_count > 300 } {
        system "$DSPMSG -s 1 $CATALOG 49 '%s: Timed out waiting for power on of %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
        nc_msg "$PROGRAM Status: error from lssyscfg command : $msg\n"
        exit 1
    }

    sleep 1
}

set done 0
set timeout 60
nc_msg "$PROGRAM Status: Check for active console.\n"

while { ! $done } {
    expect {
        -i $spawn_id_rconsole \
        -re ">" {
            nc_msg "$PROGRAM Status: active console\n"
            set done 1
        }
        timeout {
            system "$DSPMSG -s 1 $CATALOG 50 '%s: timeout; exiting\n' \"$PROGRAM\" 2>/dev/null"
            exit 1
        }
        eof {
            system "$DSPMSG -s 1 $CATALOG 11 '%s: cannot connect to %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
            exit 1
        }
    }
}

global phandle

# 
# Call get_phandle to gather information for all the supported network adapters
# in the device tree.
#
get_phandle
if { $rc } {
    system "$DSPMSG -s 1 $CATALOG 51 '%s: Unable to obtain network adapter information.  Quitting.\n' \"$PROGRAM\" 2>/dev/null"
    exit 1
}

if { $discovery } {
    system "$DSPMSG -s 1 $CATALOG 17 '# Client IP address is %s.\n' \"$client_ip\" 2>/dev/null"
    system "$DSPMSG -s 1 $CATALOG 18 '# Server IP address is %s.\n' \"$server_ip\" 2>/dev/null"
    system "$DSPMSG -s 1 $CATALOG 19 '# Gateway IP address is %s.\n' \"$gateway_ip\" 2>/dev/null"
}

if { $noboot } { # Display information for all supported adapters
    if { !$discover_macaddr || ( $discover_macaddr && $verbose ) } {
        system "$DSPMSG -s 1 $CATALOG 52 '# Getting adapter location codes.\n' 2>/dev/null"
    }

    if {[info exists list_type]} {
        set match_pat $list_type
    } else {
        set match_pat ".*"  ;# match anything
    }

    if { $discover_all } { 
        for {set i 1} {$i <= $adapter_found} {incr i 1} {
            if {[regexp $match_pat $adap_type($i)] != 0 } {
                if { $discovery } {
                    set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)]
                    nc_msg "$PROGRAM Status: ping_server returns $ping_rc\n"
                    if { $ping_rc != 0 } {
                        continue
                    }
                }

                set mac_address [get_mac_addr $phandle_array($i)]
                set loc_code [get_adaptr_loc $phandle_array($i)]
                if { $hdr_printed == 0 } {
                    system "$DSPMSG -s 1 $CATALOG 53 '# Type \tLocation Code \tMAC Address\n' 2>/dev/null"
                    set hdr_printed 1
                }
                send_user "$adap_type($i) $loc_code $mac_address\n"
            }
        }
    } else {
        for {set i 1} {$i <= $adapter_found} {incr i 1} {
	    if {[regexp $match_pat $adap_type($i)] != 0 } {
                if { $discovery } {
                    set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)]
                    nc_msg "$PROGRAM Status: ping_server returns $ping_rc\n"
                    if { $ping_rc != 0 } {
                        exit 1
                    }
                }
                set mac_address [get_mac_addr $phandle_array($i)]
                set loc_code [get_adaptr_loc $phandle_array($i)]
                system "$DSPMSG -s 1 $CATALOG 53 '# Type \tLocation Code \tMAC Address\n' 2>/dev/null"
                send_user "$adap_type($i) $loc_code $mac_address\n"
                break;
            }
        }
    }

    nc_msg "$PROGRAM Status: refreshing power status\n"

    if { !$SQUADRON && $TYPE_FULL } {
        set rc [catch {eval exec $BINPATH/lssyscfg -r sys -n $manage -Fstate} msg]
    } elseif { !$SQUADRON && !$TYPE_FULL } {
        set rc [catch {eval exec $BINPATH/lssyscfg -r lpar -m $manage -n $node -Fstate} msg]
    } elseif { $SQUADRON && !$TYPE_FULL } {
        set rc [catch {eval exec $BINPATH/lssyscfg -r lpar -m $manage --filter lpar_names=$node -Fstate} msg]
    }

    if { $rc } {
        system "$DSPMSG -s 1 $CATALOG 44 '%s: Machine state query failure detected\n' \"$PROGRAM\" 2>/dev/null"
        nc_msg "$PROGRAM Status: error from lssyscfg command\n"
        system "$DSPMSG -s 1 $CATALOG 39 '%s: Error : %s\n' \"$PROGRAM\" \"$msg\" 2>/dev/null"
        exit 1
    }

    nc_msg "$PROGRAM Status: power off the node after noboot == 1\n"
    if { !$SQUADRON && $TYPE_FULL } {
        set rc [catch {eval exec $BINPATH/chsysstate -r sys -o off -n $manage} msg]
    } elseif { !$SQUADRON && !$TYPE_FULL } {
        set rc [catch {eval exec $BINPATH/chsysstate -r lpar -o off -m $manage -n $node} msg]
    } elseif { $SQUADRON && !$TYPE_FULL } {
	set rc [catch {eval exec $BINPATH/chsysstate -r lpar -o shutdown -m $manage -n $node} msg]
    }
    
    if { $rc } {
        system "$DSPMSG -s 1 $CATALOG 42 '%s: cannot power off %s\n' \"$PROGRAM\" \"$HMCNAME\" 2>/dev/null"
        nc_msg "$PROGRAM Status: error from chsysstate command\n"
        system "$DSPMSG -s 1 $CATALOG 39 '%s: Error : %s\n' \"$PROGRAM\" \"$msg\" 2>/dev/null"
        exit 1
    }
} else {  # Do a network boot

    # Loop throught the adapters and perform a ping test to discover an
    # adapter that pings successfully, then use that adapter to network boot.
    if { $discover_all == 1 } {
        for {set i 1} {$i <= $adapter_found} {incr i 1} {
            set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)]

            # if ping was successful, then use that adapter for lpar_netboot
            if { $ping_rc == 0 } {
                set phandle $phandle_array($i)
                set full_path_name $full_path_name_array($i)
                set chosen_adap_type $adap_type($i)

                break
            } 
        }
    } elseif { $macaddress != "" } {
        set match 0
	for {set i 1} {$i <= $adapter_found} {incr i 1} {
            set mac_address [get_mac_addr $phandle_array($i)]
            if { [string compare -nocase "$mac_address" "$macaddress"] == 0 } {
                if { $discovery == 1 } {
                    set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)]
                    if { $ping_rc != 0 } {
                        system "$DSPMSG -s 1 $CATALOG 73 '%s: Unable to boot network adapter.\n' \"$PROGRAM\" 2>/dev/null"
                        exit 1
                    }
                }
                set phandle $phandle_array($i)
                set full_path_name $full_path_name_array($i)
                set chosen_adap_type $adap_type($i)
                set match 1
                break
            }
        }
        if { !$match } {
            system "$DSPMSG -s 1 $CATALOG 71 '%s: can not find mac address %s.\n' \"$PROGRAM\" \"$macaddress\" 2>/dev/null"
            exit 1
        }
    } elseif { $phys_loc != "" } {
        set match 0
        for {set i 1} {$i <= $adapter_found} {incr i 1} {
            set loc_code [get_adaptr_loc $phandle_array($i)]
            if { [string first "$phys_loc" "$loc_code"] != -1 } {
                if { $discovery == 1 } {
                    set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)]
                    if { $ping_rc != 0 } {
                        system "$DSPMSG -s 1 $CATALOG 73 '%s: Unable to boot network adapter.\n' \"$PROGRAM\" 2>/dev/null"
                        exit 1
                    }
                }
                set phandle $phandle_array($i)
                set full_path_name $full_path_name_array($i)
                set chosen_adap_type $adap_type($i)
                set match 1
                break
            }
        }
        if { !$match } {
            system "$DSPMSG -s 1 $CATALOG 72 '%s: can not find physical location %s.\n' \"$PROGRAM\" \"$phys_loc\" 2>/dev/null"
            exit 1
	}
    } else {

        #
        # Use the first ethernet adapter in the
        # device tree.
        #
        for {set i 1} {$i <= $adapter_found} {incr i 1} {
            if { $adap_type($i) == "ent" } {
                if { $discovery == 1 } {
                    set ping_rc [ping_server $full_path_name_array($i) $phandle_array($i)]
                    if { $ping_rc != 0 } {
                        exit 1
                    }
                }
                set phandle $phandle_array($i)
                set full_path_name $full_path_name_array($i)
                set chosen_adap_type $adap_type($i)
                break
            }
        }
    }

    if { $full_path_name == "" } {
        system "$DSPMSG -s 1 $CATALOG 73 '%s: Unable to boot network adapter.\n' \"$PROGRAM\" 2>/dev/null"
        exit 1
    } else {
        system "$DSPMSG -s 1 $CATALOG 54 '# Network booting install adapter.\n' 2>/dev/null"
        boot_network
    }

    if { $rc == 0 } {
        system "$DSPMSG -s 1 $CATALOG 55 '# bootp sent over network.\n' 2>/dev/null"

        Boot
    } else {
        nc_msg "return code $rc from boot_network\n"
    }
}

#
# mission accomplished, beam me up scotty.
#
if { (!$noboot) && ( $rc == 0 ) } { system "$DSPMSG -s 1 $CATALOG 56 '# Finished.\n' 2>/dev/null" }

exit $rc
